<?php
declare(strict_types=1);

namespace So\Route;

use Hyperf\Di\ReflectionManager;
use So\Annotation\Controller as SoController;
use Hyperf\HttpServer\Router\DispatcherFactory as Dispatcher;
use ReflectionMethod;

/**
 * 重写Hyperf Dispatcher
 * 对动态路由以及动态中间件支持
 * @author Saopig <1306222220@qq.com>
 */
class DispatcherFactory extends Dispatcher
{

    /**
     * 使注解路由支持So自定义中间件
     * @param array $collector
     * @throws \Hyperf\Di\Exception\ConflictAnnotationException
     */
    protected function initAnnotationRoute(array $collector): void
    {
        foreach ($collector as $className => $metadata) {
            if (isset($metadata['_c'][SoController::class])) {
                $middlewares = $this->handleMiddleware($metadata['_c']);
                $module = $this->getModule($className);
                if (!empty($module)) {
                    $_middlewares = config('so.module.' . $module . '.middlewares', []);
                    foreach ($_middlewares as $middleware) {
                        if (!class_exists($middleware)) continue;
                        $middlewares = array_merge($middlewares, [$middleware]);
                    }
                }
                $this->handleController($className, $metadata['_c'][SoController::class], $metadata['_m'] ?? [], $middlewares);
            }
        }
        parent::initAnnotationRoute($collector);
    }

    /**
     * 获取模块名
     * 命名空间中判断是否存在配置定义的模块
     * 最后一层肯定是控制器,所以除了最后一层都进行判断
     * @param string $className
     * @return string
     */
    protected function getModule(string $className): string
    {
        $classNameArr = array_filter(explode('\\', $className));
        $modules = config('so.module', []);
        foreach ($classNameArr as $k => $v) {
            $v = strtolower($v);
            if (isset($modules[$v]) && ($k + 1) !== count($classNameArr)) {
                return $v;
            }
        }
        return '';
    }

    /**
     * 让注解路由支持配置动态前缀
     * @param string $className
     * @param string $prefix
     * @return string
     */
    protected function getPrefix(string $className, string $prefix): string
    {
        $module = $this->getModule($className);
        if (!empty($module)) {
            $backend_prefix = config('so.module.' . $module . '.prefix', '');
            $backend_prefix && $prefix = '/' . trim($backend_prefix, '/') . '/' . ltrim($prefix, "/");
        }
        return parent::getPrefix($className, $prefix);
    }

}
